﻿using System;
using System.ComponentModel;
using System.IO;
using System.Net;
using System.Text;
using System.Web;
using System.Windows.Forms;
using WebDav.Client;

namespace WebDavWinFormsClientDemo
{
    public partial class DownloadForm : Form
    {
        private readonly WebClient _webClient = new WebClient();
        private bool _downloadInProgress;
        private BackgroundWorker _worker = new BackgroundWorker();

        public DownloadForm()
        {
            InitializeComponent();
        }

        public bool DownloadInProgress
        {
            get { return _downloadInProgress; }
        }

        public void DownloadResource(IResource resource, string destination)
        {
            try
            {
                buttonReady.Enabled = false;
                labelFileDownload.Text = "Datei (" + resource.DisplayName + ") wird runtergeladen: ";
                _downloadInProgress = true;

                _worker.WorkerReportsProgress = true;
                _worker.WorkerSupportsCancellation = true;
                _worker.DoWork += (object sender, DoWorkEventArgs e) =>
                    {
                        try
                        {
                            Stream sourceStream = resource.GetReadStream();
                            var fileStream = new FileStream(destination, FileMode.Create);
                            var buffer = new byte[10000];
                            long totalBytesRead = 0;
                            long bytesRead = 0;
                            DateTime begin = DateTime.Now;
                            do
                            {
                                if (_worker.CancellationPending)
                                {
                                    e.Cancel = true;
                                    Console.WriteLine("BackgroundWorker wird abgebrochen!");
                                    break;
                                }

                                bytesRead = sourceStream.Read(buffer, 0, buffer.Length);
                                if (bytesRead > 0)
                                {
                                    totalBytesRead += bytesRead;
                                    fileStream.Write(buffer, 0, (int) bytesRead);

                                    DateTime now = DateTime.Now;
                                    TimeSpan diffTime = now.Subtract(begin);
                                    if (diffTime.TotalSeconds >= 1)
                                    {
                                        var progressPercentage = (int) (totalBytesRead*100/resource.ContentLength);
                                        _worker.ReportProgress(progressPercentage, totalBytesRead);
                                        begin = DateTime.Now;
                                    }
                                }
                            } while (bytesRead > 0);
                            fileStream.Close();
                        }
                        catch (Exception exc)
                        {
                            Console.WriteLine(exc.Message);
                        }
                    };
                _worker.ProgressChanged += (object sender, ProgressChangedEventArgs e) =>
                    {
                        labelFileDownload.Text = String.Format("Datei ({0}) wird runtergeladen: {1}/{2}",
                                                               resource.DisplayName,
                                                               GetBestSizeFormat((long) e.UserState),
                                                               GetBestSizeFormat(resource.ContentLength));
                        labelPercentage.Text = e.ProgressPercentage + "%";
                        progressBarFileDownload.Value = e.ProgressPercentage;
                    };
                _worker.RunWorkerCompleted += (object sender, RunWorkerCompletedEventArgs e) =>
                    {
                        Console.WriteLine("BackgroundWorker fertig!");

                        if (e.Cancelled)
                        {
                            try
                            {
                                File.Delete(destination);
                            }
                            catch (Exception)
                            {
                            }
                        }
                        else
                        {
                            labelFileDownload.Text = String.Format("Datei ({0}) wird runtergeladen: {1}/{2}",
                                                                   resource.DisplayName,
                                                                   GetBestSizeFormat(resource.ContentLength),
                                                                   GetBestSizeFormat(resource.ContentLength));
                            labelPercentage.Text = "100%";
                        }

                        progressBarFileDownload.Value = 100;
                        _downloadInProgress = false;
                        buttonReady.Enabled = true;
                    };
                _worker.RunWorkerAsync();
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
        }

        public void DownloadFile(Uri source, string destination)
        {
            buttonReady.Enabled = false;
            labelFileDownload.Text = "Datei (" + source.Segments[source.Segments.Length - 1] + ") wird runtergeladen: ";
            _downloadInProgress = true;
            _webClient.DownloadProgressChanged += (object sender, DownloadProgressChangedEventArgs e) =>
                {
                    labelFileDownload.Text = String.Format("Datei ({0}) wird runtergeladen: {1}/{2}",
                                                           source.Segments[source.Segments.Length - 1],
                                                           GetBestSizeFormat(e.BytesReceived),
                                                           GetBestSizeFormat(e.TotalBytesToReceive));
                    labelPercentage.Text = e.ProgressPercentage.ToString() + "%";
                    progressBarFileDownload.Value = e.ProgressPercentage;
                };
            _webClient.DownloadFileCompleted += (object sender, AsyncCompletedEventArgs e) =>
                {
                    if (!e.Cancelled)
                    {
                        progressBarFileDownload.Value = 100;
                    }
                    else
                    {
                        try
                        {
                            File.Delete(destination);
                        }
                        catch (Exception)
                        {
                        }
                    }
                    _downloadInProgress = false;
                    buttonReady.Enabled = true;
                };
            _webClient.DownloadFileAsync(source, destination);
        }

        public void UploadFile(string source, IResource destinationResource)
        {
            buttonReady.Enabled = false;
            labelFileDownload.Text = "Datei (" + destinationResource.DisplayName + ") wird hochgeladen: ";
            _downloadInProgress = true;

            var file = new FileInfo(source);
            _worker = new BackgroundWorker();
            _worker.WorkerReportsProgress = true;
            _worker.WorkerSupportsCancellation = true;
            _worker.DoWork += (object sender, DoWorkEventArgs e) =>
                {
                    Console.WriteLine("BackgroundWorker gestartet");

                    try
                    {
                        using (
                            Stream webStream = destinationResource.GetWriteStream("application/octet-stream",
                                                                                  file.Length))
                        {
                            int bufSize = 10240; // 10Kb
                            var buffer = new byte[bufSize];
                            int bytesRead = 0;
                            long totalBytesRead = 0;
                            DateTime begin = DateTime.Now;
                            using (FileStream fileStream = file.OpenRead())
                            {
                                do
                                {
                                    if (_worker.CancellationPending)
                                    {
                                        e.Cancel = true;
                                        Console.WriteLine("BackgroundWorker wird abgebrochen!");
                                        break;
                                    }

                                    bytesRead = fileStream.Read(buffer, 0, bufSize);
                                    if (bytesRead > 0)
                                    {
                                        try
                                        {
                                            webStream.Write(buffer, 0, bytesRead);
                                            totalBytesRead += bytesRead;
                                            if (DateTime.Now.Subtract(begin).TotalSeconds >= 1)
                                            {
                                                var ProgressPercentage = (int) (totalBytesRead*100/file.Length);
                                                _worker.ReportProgress(ProgressPercentage, totalBytesRead);
                                                begin = DateTime.Now;
                                            }
                                        }
                                        catch (Exception writeException)
                                        {
                                            e.Cancel = true;
                                            Console.WriteLine("Verbindung wurde unerwartet unterbrochen! (" +
                                                              totalBytesRead + " bytes)");
                                            MessageBox.Show(writeException.Message);
                                            break;
                                        }
                                    }
                                } while (bytesRead > 0);
                            }

                            string result = "";
                            var buffer2 = new byte[8192];
                            int bytesRead2 = 0;
                            do
                            {
                                bytesRead2 = webStream.Read(buffer2, 0, buffer2.Length);
                                if (bytesRead2 > 0)
                                {
                                    result += Encoding.UTF8.GetString(buffer2, 0, bytesRead2);
                                }
                            } while (bytesRead2 > 0);
                            if (result != String.Empty)
                            {
                                Console.WriteLine(result);
                            }
                        }
                    }
                    catch (Exception exc)
                    {
                        e.Cancel = true;
                        MessageBox.Show(exc.Message);
                    }
                };
            _worker.ProgressChanged += (object sender, ProgressChangedEventArgs e) =>
                {
                    int ProgressPercentage = Convert.ToInt32((long) e.UserState*100.0/file.Length);
                    labelFileDownload.Text = String.Format("Datei ({0}) wird hochgeladen: {1}/{2}",
                                                           destinationResource.DisplayName,
                                                           GetBestSizeFormat((long) e.UserState),
                                                           GetBestSizeFormat(file.Length));
                    labelPercentage.Text = ProgressPercentage.ToString() + "%";
                    progressBarFileDownload.Value = ProgressPercentage;
                };
            _worker.RunWorkerCompleted += (object sender, RunWorkerCompletedEventArgs e) =>
                {
                    Console.WriteLine("BackgroundWorker fertig!");

                    if (!e.Cancelled)
                    {
                        progressBarFileDownload.Value = 100;
                        labelFileDownload.Text = String.Format("Datei ({0}) wird hochgeladen: {1}/{2}",
                                                               destinationResource.DisplayName,
                                                               GetBestSizeFormat(file.Length),
                                                               GetBestSizeFormat(file.Length));
                        labelPercentage.Text = "100%";
                    }
                    _downloadInProgress = false;
                    buttonReady.Enabled = true;
                };
            _worker.RunWorkerAsync();
        }

        public void UploadFile(string source, Uri destination)
        {
            buttonReady.Enabled = false;
            labelFileDownload.Text = "Datei (" + destination.Segments[destination.Segments.Length - 1] +
                                     ") wird hochgeladen: ";
            _downloadInProgress = true;
            _webClient.UploadProgressChanged += (object sender, UploadProgressChangedEventArgs e) =>
                {
                    int ProgressPercentage = Convert.ToInt32(e.BytesSent*100.0/e.TotalBytesToSend);
                    labelFileDownload.Text = String.Format("Datei ({0}) wird hochgeladen: {1}/{2}",
                                                           HttpUtility.UrlDecode(
                                                               destination.Segments[destination.Segments.Length - 1]),
                                                           GetBestSizeFormat(e.BytesSent),
                                                           GetBestSizeFormat(e.TotalBytesToSend));
                    labelPercentage.Text = ProgressPercentage.ToString() + "%";
                    progressBarFileDownload.Value = ProgressPercentage;
                };
            _webClient.UploadFileCompleted += (object sender, UploadFileCompletedEventArgs e) =>
                {
                    if (!e.Cancelled)
                    {
                        progressBarFileDownload.Value = 100;
                    }
                    _downloadInProgress = false;
                    buttonReady.Enabled = true;
                };
            _webClient.UploadFileAsync(destination, "PUT", source);
        }

        private string GetBestSizeFormat(long size)
        {
            string sizeFormat = "";

            try
            {
                if (size > 1000000000000)
                {
                    sizeFormat = (size/1099511627776.0).ToString("f") + "TB";
                }
                else if (size > 1000000000)
                {
                    sizeFormat = (size/(1024.0*1024.0*1024.0)).ToString("f") + "GB";
                }
                else if (size > 1000000)
                {
                    sizeFormat = (size/(1024.0*1024.0)).ToString("f") + "MB";
                }
                else if (size > 1000)
                {
                    sizeFormat = (size/1024.0).ToString("f") + "KB";
                }
                else
                {
                    sizeFormat = size.ToString() + "B";
                }
            }
            catch (Exception)
            {
                sizeFormat = size.ToString();
            }

            return sizeFormat;
        }

        private void DownloadForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (_downloadInProgress)
            {
                MessageBox.Show("Dieses Fenster kann während des Downloads nicht geschlossen werden!");
                e.Cancel = true;
            }
        }

        private void buttonReady_Click(object sender, EventArgs e)
        {
            Close();
        }

        private void buttonCancel_Click(object sender, EventArgs e)
        {
            DialogResult result = MessageBox.Show("Willst du den Download wirklich abbrechen?", "Download Abbruch",
                                                  MessageBoxButtons.YesNo, MessageBoxIcon.Question);
            if (result == DialogResult.Yes)
            {
                _webClient.CancelAsync();
                _worker.CancelAsync();
            }
        }
    }
}